package cn.kennylee.codehub.feign.interceptor.caller.remote.interceptors;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONUtil;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.*;

/**
 * <p> 转发header的feign拦截器 </p>
 * <p>Created on 2024/1/23.</p>
 *
 * @author kennylee
 * @since 0.0.1
 */
@Slf4j
public class ForwardHeadersInterceptor implements RequestInterceptor, Ordered {

    /**
     * 默认阻塞的请求头
     */
    private static final Set<String> DEFAULT_BLOCK_HEADERS = CollUtil.newHashSet();

    @Override
    public void apply(RequestTemplate requestTemplate) {

        CustomFeignContext feignContext = FeignContextHolder.getFeignContext();

        if (!feignContext.isForwardHeaders()) {
            log.debug("当前Feign上下文未开启转发header功能，跳过转发header");
            return;
        }

        final Map<String, String> forwardHeaders = MapUtil.newHashMap();
        // 获取当前请求的包含的原始请求headers
        forwardHeaders.putAll(getOriginHeaders());

        try {
            // 删除不需要转发的请求头
            removeBlockHeaders(forwardHeaders);
            // 添加自定义的请求头
            forwardHeaders.putAll(feignContext.getHeaders());

            if (!forwardHeaders.isEmpty()) {
                if (log.isDebugEnabled()) {
                    log.debug("额外转发的请求头: {}", JSONUtil.toJsonStr(forwardHeaders));
                }
                forwardHeaders.forEach(requestTemplate::header);
            }
        } finally {
            if (feignContext.isClearAtOnce()) {
                // 清除自定义Feign上下文，避免请求头泄漏
                FeignContextHolder.clear();
            } else {
                log.debug("Feign上下文未清除，请求头可能会在后续请求中继续存在，请自行管理清除时机");
            }
        }
    }

    /**
     * 删除不需要转发的请求头
     *
     * @param forwardHeaders 需要转发的请求头
     */
    private static void removeBlockHeaders(Map<String, String> forwardHeaders) {
        if (CollUtil.isEmpty(forwardHeaders) || CollUtil.isEmpty(getBlockHeaders())) {
            return;
        }
        // 忽略大小写，删除不需要转发的请求头
        Collection<String> blockHeaders = getBlockHeaders();
        forwardHeaders.keySet().removeIf(headerName -> blockHeaders.stream().anyMatch(blockHeader -> blockHeader.equalsIgnoreCase(headerName)));
    }

    /**
     * 获取需要阻塞的请求头
     *
     * @return 阻塞的请求头列表
     */
    private static Collection<String> getBlockHeaders() {
        Set<String> blockHeaders = new HashSet<>();
        blockHeaders.addAll(getDefaultBlockHeaders());
        blockHeaders.addAll(FeignContextHolder.getFeignContext().getBlockHeaders());
        return blockHeaders;
    }

    /**
     * 获取当前请求的原始header
     *
     * @return 原始header列表
     */
    @NonNull
    private static Map<String, String> getOriginHeaders() {
        final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        if (Objects.isNull(requestAttributes)) {
            log.debug("当前请求上下文不存在，转发的原始header为空");
            return Map.of();
        }

        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();

        Enumeration<String> headerNames = httpServletRequest.getHeaderNames();

        Map<String, String> forwardHeaders = MapUtil.newHashMap();

        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();

            String headerValue = httpServletRequest.getHeader(headerName);
            if (StringUtils.hasLength(headerValue)) {
                log.trace("headerName: {}, headerValue: {}", headerName, headerValue);
                if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) {
                    continue;
                }
                forwardHeaders.put(headerName, headerValue);
            }
        }

        return Map.copyOf(forwardHeaders);
    }

    @Override
    public int getOrder() {
        return FeignRequestLogInterceptor.ORDER - 1;
    }

    /**
     * 添加默认阻塞头部
     *
     * @param headerName 头部名称
     */
    public static void addDefaultBlockHeader(String headerName) {
        DEFAULT_BLOCK_HEADERS.add(headerName);
    }

    /**
     * 移除默认阻塞头部
     *
     * @param headerName 头部名称
     * @return 是否移除成功
     */
    public static boolean removeDefaultBlockHeader(String headerName) {
        return DEFAULT_BLOCK_HEADERS.remove(headerName);
    }

    /**
     * 获取默认阻塞头部列表
     *
     * @return 默认阻塞头部列表
     */
    public static Set<String> getDefaultBlockHeaders() {
        return Collections.unmodifiableSet(DEFAULT_BLOCK_HEADERS);
    }
}
